package manager

import (
	"context"
	"fmt"
	"quant/backend/constants"
	"quant/backend/entity"
	"quant/backend/indicator"
	"sort"
	"time"

	"github.com/duke-git/lancet/v2/maputil"
	"github.com/duke-git/lancet/v2/mathutil"
	"github.com/duke-git/lancet/v2/slice"
)

type DailyManager struct {
	// 引入公共类
	// baseService
	*BaseManager[entity.DailyEntity]
}

func NewDailyManager() *DailyManager {
	return &DailyManager{
		BaseManager: newManager[entity.DailyEntity](),
	}
}

// FindMapForStockCode 把daily数据按股票代码进行分组（Map）
func (dm *DailyManager) FindMapForStockCode() (map[string][]entity.DailyEntity, error) {
	entities, err := dm.FindAll()
	if err != nil {
		return nil, err
	}
	var dailyMap = make(map[string][]entity.DailyEntity)
	for _, daily := range entities {
		dailyMap[daily.StockCode] = append(dailyMap[daily.StockCode], daily)
	}
	return dailyMap, nil
}

func (dm *DailyManager) UpdateMAn(startDate, endDate string) error {
	allStartTime := time.Now()

	constants.App.Event.Emit("logs", "获取样本数据... ")
	// 获取股票代码与日行情数据的映射关系
	m, err := dm.FindMapForStockCode()
	if err != nil {
		constants.App.Event.Emit("logs", fmt.Sprintf("失败！>> %s\n", err.Error()))
		return err
	}
	constants.App.Event.Emit("logs", "完成！\n")

	maputil.ForEach(m, func(key string, items []entity.DailyEntity) {
		if len(items) <= 0 {
			return
		}
	})

	// startTime := time.Now() // 获取开始时间，用于统计耗时
	// constants.App.Event.Emit("logs", fmt.Sprintf("[%s]开始计算...", d))
	// affected, err := dm.WithTx(context.Background(), func(svc *BaseManager[entity.DailyEntity]) (int64, error) {
	// startIndex, endIndex := 0, 0
	// if startDate == "" || endDate == "" {
	// 	startIndex = 0
	// 	endIndex = len(items) - 1
	// } else if startDate == endDate {
	// 	idx := slice.IndexOf(dateList, startDate)
	// 	if idx >= 0 {
	// 		startIndex = idx
	// 		endIndex = idx
	// 	}
	// } else {
	// 	startIndex = slice.IndexOf(dateList, startDate)
	// 	endIndex = slice.IndexOf(dateList, startDate)
	// 	if startIndex < 0 || endIndex < 0 {
	// 		startIndex = 0
	// 		endIndex = len(items) - 1
	// 	}
	// }
	// var count int64
	// 	maputil.ForEach(m, func(key string, items []entity.DailyEntity) {
	// 		de, ok := slice.FindBy(items, func(index int, item entity.DailyEntity) bool {
	// 			return strings.EqualFold(item.Time, d)
	// 		})
	// 		if !ok {
	// 			return
	// 		}
	// 		entities := slice.Filter(items, func(index int, item entity.DailyEntity) bool {
	// 			return item.Time < d
	// 		})
	//
	// 		if len(entities) <= 0 {
	// 			de.Ma5 = de.PreClose
	// 			de.Ma5 = de.PreClose
	// 			de.Ma10 = de.PreClose
	// 			return
	// 		} else {
	// 			_ = slice.SortByField(entities, "Time", "desc")
	// 			// 把价格从切面数组中取出来，转换成float64切片
	// 			prices := slice.Map(entities, func(index int, item entity.DailyEntity) float64 {
	// 				return item.Close
	// 			})
	// 			de.Ma5 = mathutil.RoundToFloat(indicator.NewMA(5).Update(prices), constants.Precision6)
	// 			de.Ma10 = mathutil.RoundToFloat(indicator.NewMA(10).Update(prices), constants.Precision6)
	// 			de.Ma20 = mathutil.RoundToFloat(indicator.NewMA(20).Update(prices), constants.Precision6)
	// 		}
	//
	// 		num, err2 := svc.Update([]string{"ma5", "ma10", "ma20"}, de.Id, &de)
	// 		if err2 != nil {
	// 			return
	// 		}
	// 		count = count + num
	// 	})
	// return count, nil
	// })
	if err != nil {
		constants.App.Event.Emit("logs", fmt.Sprintf("->失败！%v！\n", err))
		return err
	}

	// endTime := time.Now()
	// diff := endTime.Sub(startTime)
	// constants.App.Event.Emit("logs", fmt.Sprintf("-->完成!,更新记录数: %d, 耗时(秒):%f \n", affected, mathutil.RoundToFloat(diff.Seconds(), constants.Precision2)))
	// time.Sleep(200 * time.Millisecond)
	// }
	allEndTime := time.Now()
	diff := allEndTime.Sub(allStartTime)
	constants.App.Event.Emit("logs", fmt.Sprintf("MAn 计算完成,总耗时(秒):%f\n", mathutil.RoundToFloat(diff.Seconds(), constants.Precision2)))
	return nil
}

// CalculateMACD 计算指定日期范围内所有股票的 MACD 指标，并更新到数据库中。
// 参数:
//   - startDate: 开始日期，格式为 "YYYY-MM-DD"。若为空字符串，则从第一条数据开始计算。
//   - endDate: 结束日期，格式为 "YYYY-MM-DD"。若为空字符串，则计算到最后一条数据。
//
// 返回值:
//   - error: 如果在获取数据、计算或更新过程中发生错误，则返回相应的错误信息；否则返回 nil。
func (dm *DailyManager) CalculateMACD(startDate, endDate string) error {
	allStartTime := time.Now()

	constants.App.Event.Emit("logs", "获取样本数据... ")
	// 获取股票代码与日行情数据的映射关系
	m, err := dm.FindMapForStockCode()
	if err != nil {
		constants.App.Event.Emit("logs", fmt.Sprintf("失败！>> %s\n", err.Error()))
		return err
	}
	constants.App.Event.Emit("logs", "完成！\n")

	// 遍历每只股票的数据进行 MACD 计算
	maputil.ForEach(m, func(key string, items []entity.DailyEntity) {
		if len(items) <= 0 {
			return
		}
		// 提取并排序日期列表
		dateList := slice.Map(items, func(index int, item entity.DailyEntity) string {
			return item.Time
		})
		sort.Strings(dateList) // 日期升序

		// 对日行情数据按照时间字段升序排列
		err = slice.SortByField(items, "Time", "asc")
		if err != nil {
			return
		}

		startTime := time.Now() // 获取开始时间，用于统计耗时
		constants.App.Event.Emit("logs", fmt.Sprintf("[%s]开始计算...", key))

		var lastMacd *indicator.MACD
		// 使用事务批量更新计算结果
		affected, err := dm.WithTx(context.Background(), func(bm *BaseManager[entity.DailyEntity]) (int64, error) {
			startIndex, endIndex := 0, 0
			// 根据传入的起止日期确定处理范围
			if startDate == "" || endDate == "" {
				startIndex = 0
				endIndex = len(items) - 1
			} else if startDate == endDate {
				idx := slice.IndexOf(dateList, startDate)
				if idx >= 0 {
					startIndex = idx
					endIndex = idx
				}
			} else {
				startIndex = slice.IndexOf(dateList, startDate)
				endIndex = slice.IndexOf(dateList, startDate)
				if startIndex < 0 || endIndex < 0 {
					startIndex = 0
					endIndex = len(items) - 1
				}
			}

			var count int64
			// 遍历指定范围内的每日行情数据，逐条计算 MACD 并更新数据库
			for idx := startIndex; idx <= endIndex; idx++ {
				de := items[idx]
				if idx == startIndex {
					// 处理第一个数据点：初始化 MACD 指标
					if idx == 0 {
						macd := indicator.NewMACD(6, 13, 5)
						dif, dea, ma := macd.Update(de.Close, nil)
						de.Ema1 = mathutil.RoundToFloat(de.Close, constants.Precision6)
						de.Ema2 = mathutil.RoundToFloat(de.Close, constants.Precision6)
						de.Ema3 = mathutil.RoundToFloat(de.Close, constants.Precision6)
						de.Dif = mathutil.RoundToFloat(dif, constants.Precision6)
						de.Dea = mathutil.RoundToFloat(dea, constants.Precision6)
						de.Macd = mathutil.RoundToFloat(ma, constants.Precision6)
						lastMacd = macd
					} else {
						// 基于前一个数据点恢复 MACD 状态
						lastDe := items[idx-1]
						lastMacd = indicator.NewMACD2(
							indicator.NewEMA2(6, lastDe.Ema1),
							indicator.NewEMA2(13, lastDe.Ema2),
							indicator.NewEMA2(5, lastDe.Ema3),
						)

						macd := indicator.NewMACD(6, 13, 5)
						dif, dea, ma := macd.Update(de.Close, lastMacd)
						de.Ema1 = mathutil.RoundToFloat(macd.GetShort().GetValue(), constants.Precision6)
						de.Ema2 = mathutil.RoundToFloat(macd.GetLong().GetValue(), constants.Precision6)
						de.Ema3 = mathutil.RoundToFloat(macd.GetSignal().GetValue(), constants.Precision6)
						de.Dif = mathutil.RoundToFloat(dif, constants.Precision6)
						de.Dea = mathutil.RoundToFloat(dea, constants.Precision6)
						de.Macd = mathutil.RoundToFloat(ma, constants.Precision6)
						lastMacd = macd
					}
				} else {
					// 继续使用上一次的 MACD 状态进行计算
					macd := indicator.NewMACD(6, 13, 5)
					dif, dea, ma := macd.Update(de.Close, lastMacd)
					de.Ema1 = mathutil.RoundToFloat(macd.GetShort().GetValue(), constants.Precision6)
					de.Ema2 = mathutil.RoundToFloat(macd.GetLong().GetValue(), constants.Precision6)
					de.Ema3 = mathutil.RoundToFloat(macd.GetSignal().GetValue(), constants.Precision6)
					de.Dif = mathutil.RoundToFloat(dif, constants.Precision6)
					de.Dea = mathutil.RoundToFloat(dea, constants.Precision6)
					de.Macd = mathutil.RoundToFloat(ma, constants.Precision6)
					lastMacd = macd
				}

				// 更新数据库中的 MACD 相关字段
				num, err := bm.Update([]string{"ema1", "ema2", "ema3", "macd", "dif", "dea"}, de.Id, &de)
				if err != nil {
					return 0, err
				}
				count = count + num
			}
			return count, nil
		})

		if err != nil {
			constants.App.Event.Emit("logs", fmt.Sprintf("失败！%v！\n", err))
			return
		}

		endTime := time.Now()
		diff := endTime.Sub(startTime)
		constants.App.Event.Emit("logs", fmt.Sprintf("完成!,更新记录数: %d, 耗时(秒):%f \n", affected, mathutil.RoundToFloat(diff.Seconds(), constants.Precision2)))
		time.Sleep(10 * time.Millisecond)
	})

	allEndTime := time.Now()
	diff := allEndTime.Sub(allStartTime)
	constants.App.Event.Emit("logs", fmt.Sprintf("MACD 计算完成,总耗时(秒):%f\n", mathutil.RoundToFloat(diff.Seconds(), constants.Precision2)))
	return nil
}

func (dm *DailyManager) resetMACD() error {
	allStartTime := time.Now()

	constants.App.Event.Emit("logs", "获取样本数据... ")
	m, err := dm.FindMapForStockCode()
	if err != nil {
		constants.App.Event.Emit("logs", fmt.Sprintf("失败！>> %s\n", err.Error()))
		return err
	}
	constants.App.Event.Emit("logs", "完成！\n")

	maputil.ForEach(m, func(key string, items []entity.DailyEntity) {
		if len(items) <= 0 {
			return
		}
		// 获取某只股票日行情的日期列表
		dateList := slice.Map(items, func(index int, item entity.DailyEntity) string {
			return item.Time
		})
		sort.Strings(dateList) // 日期升序
		// 日行情升序
		err = slice.SortByField(items, "Time", "asc")
		if err != nil {
			return
		}
		// startTime := time.Now() // 获取开始时间，用于统计耗时
		// constants.App.Event.Emit("logs", fmt.Sprintf("[%s]开始计算...", key))
		startTime := time.Now() // 获取开始时间，用于统计耗时
		constants.App.Event.Emit("logs", fmt.Sprintf("[%s]开始计算...", key))
		var lastMacd *indicator.MACD
		affected, err := dm.WithTx(context.Background(), func(svc *BaseManager[entity.DailyEntity]) (int64, error) {
			var count int64
			for i, _ := range dateList {
				de := items[i]
				if i == 0 {
					macd := indicator.NewMACD(6, 13, 5)
					dif, dea, ma := macd.Update(de.Close, nil)
					de.Ema1 = mathutil.RoundToFloat(de.Close, constants.Precision6)
					de.Ema2 = mathutil.RoundToFloat(de.Close, constants.Precision6)
					de.Dif = mathutil.RoundToFloat(dif, constants.Precision6)
					de.Dea = mathutil.RoundToFloat(dea, constants.Precision6)
					de.Macd = mathutil.RoundToFloat(ma, constants.Precision6)
					lastMacd = macd
				} else {
					macd := indicator.NewMACD(6, 13, 5)
					dif, dea, ma := macd.Update(de.Close, lastMacd)
					de.Ema1 = mathutil.RoundToFloat(macd.GetShort().GetValue(), constants.Precision6)
					de.Ema2 = mathutil.RoundToFloat(macd.GetLong().GetValue(), constants.Precision6)
					de.Dif = mathutil.RoundToFloat(dif, constants.Precision6)
					de.Dea = mathutil.RoundToFloat(dea, constants.Precision6)
					de.Macd = mathutil.RoundToFloat(ma, constants.Precision6)
					lastMacd = macd
				}
				num, err := svc.Update([]string{"ema1", "ema2", "macd", "dif", "dea"}, de.Id, &de)
				if err != nil {
					return 0, err
				}
				count = count + num
			}
			return count, nil
		})
		if err != nil {
			constants.App.Event.Emit("logs", fmt.Sprintf("失败！%v！\n", err))
			return
		}
		endTime := time.Now()
		diff := endTime.Sub(startTime)
		constants.App.Event.Emit("logs", fmt.Sprintf("完成!,更新记录数: %d, 耗时(秒):%f \n", affected, mathutil.RoundToFloat(diff.Seconds(), constants.Precision2)))
		time.Sleep(10 * time.Millisecond)
	})
	allEndTime := time.Now()
	diff := allEndTime.Sub(allStartTime)
	constants.App.Event.Emit("logs", fmt.Sprintf("MACD 计算完成,总耗时(秒):%f\n", mathutil.RoundToFloat(diff.Seconds(), constants.Precision2)))
	return nil
}

// func (s *DailyManager) UpdateMACD(dateList []string) error {
// allStartTime := time.Now()
//
// constants.App.Event.Emit("logs", "获取样本数据... ")
// m, err := s.FindMapForStockCode()
// if err != nil {
// 	constants.App.Event.Emit("logs", fmt.Sprintf("失败！>> %s\n", err.Error()))
// 	return err
// }
// constants.App.Event.Emit("logs", "完成！\n")
//
// for idx, d := range dateList {
// 	// 如果当前交易日是今天，则跳过
// 	if strings.EqualFold(d, time.Now().Format(constants.TimeLayout)) {
// 		continue
// 	}
//
// 	startTime := time.Now() // 获取开始时间，用于统计耗时
// 	constants.App.Event.Emit("logs", fmt.Sprintf("[%s]开始计算...", d))
// 	affected, err := s.WithTx(context.Background(), func(svc *BaseManager[entity.DailyEntity]) (int64, error) {
// 		var affected int64
// 		var lastMacd *indicator.MACD
// 		maputil.ForEach(m, func(key string, items []entity.DailyEntity) {
// 			// 第一个日期
// 			// if idx==0 {
// 			//
// 			// }
// 			de, ok := slice.FindBy(items, func(index int, item entity.DailyEntity) bool {
// 				return strings.EqualFold(item.Time, d)
// 			})
// 			if !ok {
// 				return
// 			}
// 			entities := slice.Filter(items, func(index int, item entity.DailyEntity) bool {
// 				return item.Time < d
// 			})
//
// 			if len(entities) <= 0 {
// 				macd := indicator.NewMACD(6, 13, 5)
// 				lastMacd = macd
// 				dif, dea, ma := macd.Update(de.Close, nil)
// 				de.Ema1 = macd.GetShort().GetValue()
// 				de.Ema2 = macd.GetLong().GetValue()
// 				de.Dif = dif
// 				de.Dea = dea
// 				de.Macd = ma
// 				return
// 			} else {
// 				_ = slice.SortByField(entities, "Time", "desc")
// 				// 把价格从切面数组中取出来，转换成float64切片
// 				prices := slice.Map(entities, func(index int, item entity.DailyEntity) float64 {
// 					return item.Close
// 				})
// 				de.Ma5 = mathutil.RoundToFloat(indicator.NewMA(5).Update(prices), constants.Precision6)
// 				de.Ma10 = mathutil.RoundToFloat(indicator.NewMA(10).Update(prices), constants.Precision6)
// 				de.Ma20 = mathutil.RoundToFloat(indicator.NewMA(20).Update(prices), constants.Precision6)
// 			}
//
// 			num, err2 := svc.Update([]string{"ma5", "ma10", "ma20"}, de.Id, &de)
// 			if err2 != nil {
// 				return
// 			}
// 			affected = affected + num
// 		})
// 		return affected, nil
// 	})
// 	if err != nil {
// 		constants.App.Event.Emit("logs", fmt.Sprintf("->失败！%v！\n", err))
// 		continue
// 	}
//
// 	endTime := time.Now()
// 	diff := endTime.Sub(startTime)
// 	constants.App.Event.Emit("logs", fmt.Sprintf("-->完成!,更新记录数: %d, 耗时(秒):%f \n", affected, mathutil.RoundToFloat(diff.Seconds(), constants.Precision2)))
// 	time.Sleep(200 * time.Millisecond)
// }
// allEndTime := time.Now()
// diff := allEndTime.Sub(allStartTime)
// constants.App.Event.Emit("logs", fmt.Sprintf("MAn 计算完成,总耗时(秒):%f\n", mathutil.RoundToFloat(diff.Seconds(), constants.Precision2)))
// return nil
// }
